package com.pl.escape;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.mapping.BoundSql;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public abstract class AbstractLikeSqlConverter<T> {
    private static final Logger log = LoggerFactory.getLogger(AbstractLikeSqlConverter.class);


    protected static final String LIKE_SQL_KEY = "%";


    protected static final String[] ESCAPE_CHAR = new String[]{"%", "_", "\\"};


    protected static final String MYBATIS_PLUS_LIKE_SQL = " like ?";


    protected static final String MYBATIS_PLUS_WRAPPER_PREFIX = "ew.paramNameValuePairs.";


    protected static final String MYBATIS_PLUS_WRAPPER_KEY = "ew";


    protected static final String MYBATIS_PLUS_WRAPPER_SEPARATOR = ".";


    protected static final String MYBATIS_PLUS_WRAPPER_SEPARATOR_REGEX = "\\.";


    protected static final String REPLACED_LIKE_KEYWORD_MARK = "replaced.keyword";


    protected static final String MYBATIS_FOREACH_PARAM = "__frch_item_";


    public void convert(String sql, Set<String> fields, T parameter, BoundSql boundSql) {
        for (String field : fields) {
            if (hasMybatisPlusLikeSql(sql)) {
                if (hasWrapper(field)) {

                    transferWrapper(field, parameter);

                    continue;
                }
                transferSelf(field, parameter);

                continue;
            }

            transferSplice(field, parameter, boundSql);
        }
    }


    public abstract void transferWrapper(String paramString, T paramT);


    public abstract void transferSelf(String paramString, T paramT);


    public abstract void transferSplice(String paramString, T paramT, BoundSql paramBoundSql);


    String escapeChar(String before) {
        if (StringUtils.isNotBlank(before)) {
            before = before.replaceAll("\\\\", "\\\\\\\\");
            before = before.replaceAll("_", "\\\\_");
            before = before.replaceAll("%", "\\\\%");
        }
        return before;
    }


    boolean hasEscapeChar(Object obj) {
        if (!(obj instanceof String)) {
            return false;
        }
        return hasEscapeChar((String) obj);
    }


    void resolveObj(String field, Object parameter) {
        if (parameter == null || StringUtils.isBlank(field)) {
            return;
        }
        try {
            PropertyDescriptor descriptor = new PropertyDescriptor(field, parameter.getClass());
            Method readMethod = descriptor.getReadMethod();
            Object param = readMethod.invoke(parameter, new Object[0]);
            if (hasEscapeChar(param)) {
                Method setMethod = descriptor.getWriteMethod();
                setMethod.invoke(parameter, new Object[]{escapeChar(param.toString())});
            } else if (cascade(field)) {
                int index = field.indexOf(".") + 1;
                resolveObj(field.substring(index), param);
            }

        } catch (IntrospectionException | IllegalAccessException | java.lang.reflect.InvocationTargetException e) {
            log.error("反射 {} 的 {} get/set方法出现异常", new Object[]{parameter, field, e});
        }
    }


    boolean cascade(String field) {
        if (StringUtils.isBlank(field)) {
            return false;
        }
        return (field.contains(".") && !hasWrapper(field));
    }


    private boolean hasMybatisPlusLikeSql(String sql) {
        if (StringUtils.isBlank(sql)) {
            return false;
        }
        return sql.toLowerCase().contains(" like ?");
    }


    private boolean hasWrapper(String field) {
        if (StringUtils.isBlank(field)) {
            return false;
        }
        return field.contains("ew.paramNameValuePairs.");
    }


    private boolean hasEscapeChar(String str) {
        if (StringUtils.isBlank(str)) {
            return false;
        }
        for (String s : ESCAPE_CHAR) {
            if (str.contains(s)) {
                return true;
            }
        }
        return false;
    }
}


